package com.linhngua.code.processing.graph;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import processing.core.PApplet;
import static com.linhngua.code.processing.graph.Constants.*;
import static com.linhngua.code.processing.graph.Util.*;

public class Grapher extends PApplet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static final NumberFormat NUMBER_FORMAT = new DecimalFormat("#.##");

	private FunctionProcessor functionProcessor;

	private Function function;
	


	// set up the window
	@Override
	public void setup() {

		super.setup();
		size(Constants.WINDOW_WIDTH, Constants.WINDOW_HEIGHT);
		strokeWeight(2);
		smooth();
		frameRate(15);
		float d = 0.1f;

		function = new Function() {

			@Override
			public float nextY(float x) {

				return (float) (Math.pow(x,5)+Math.pow(x,3)+Math.pow(x,2)+50);
			}
		};
		functionProcessor = new FunctionProcessorImpl(d, function, 0.01f,1f);
	}

	@Override
	public void draw() {
		background(100);
		drawAxis();
		MathPoint[] points = functionProcessor.getGraph();
		strokeWeight(1f);
		float scaleY = functionProcessor.scaleY();
		float scaleX = functionProcessor.scaleX();
		for (int i = 0; i < points.length - 1; i++) {
			MathPoint p1 = points[i];
			MathPoint p2 = points[i + 1];
			drawLine(p1, p2, scaleX, scaleY);

		}
		
		//draw mouse position
		GraphPoint mouseP=new GraphPoint(mouseX,mouseY);
		MathPoint mathPoint=toMathPoint(mouseP, scaleX, scaleY);
		String x=NUMBER_FORMAT.format(mathPoint.x);
		String y=NUMBER_FORMAT.format(mathPoint.y);
		text("(x="+x+","+"y="+y+")",mouseX,mouseY);

	}

	void drawLine(MathPoint p1, MathPoint p2, float scaleX, float scaleY) {

		GraphPoint g1 = toGraphPoint(p1, scaleX, scaleY);
		GraphPoint g2 = toGraphPoint(p2, scaleX, scaleY);
		line(g1.x, g1.y, g2.x, g2.y);

	}

	// draw x and y axis
	final void drawAxis() {
		strokeWeight(1f);
		// x axis
		drawArrow(new MathPoint(-WINDOW_WIDTH / 2 - 10, 0f), new MathPoint(
				WINDOW_WIDTH / 2 - 10, 0f));

		float scaleX = functionProcessor.scaleX();
		for (float x = -300f; x <= 300f; x = x + 100) {
			MathPoint p = new MathPoint(x, 0);
			drawNodeX(p, x * scaleX);
		}

		// y axis
		drawArrow(new MathPoint(0f, -WINDOW_HEIGHT / 2 - 10), new MathPoint(0f,
				WINDOW_HEIGHT / 2 - 50));

		float scaleY = functionProcessor.scaleY();
		for (float y = -300f; y <= 300f; y = y + 100f) {
			MathPoint p = new MathPoint(0, y);
			if ( y != 0) {
				drawNodeY(p, y / scaleY);
			}
		}

	}

	void drawNodeX(MathPoint p, float num) {

		GraphPoint g1 = toGraphPoint(new MathPoint(p.x, p.y + 10));
		GraphPoint g2 = toGraphPoint(new MathPoint(p.x, p.y - 10));
		line(g1.x, g1.y, g2.x, g2.y);
		GraphPoint text = toGraphPoint(new MathPoint(p.x + 5, p.y - 20));
		String numString = NUMBER_FORMAT.format(num);
		text(numString, text.x, text.y);
	}

	void drawNodeY(MathPoint p, float num) {

		GraphPoint g1 = toGraphPoint(new MathPoint(p.x - 10, p.y));
		GraphPoint g2 = toGraphPoint(new MathPoint(p.x + 10, p.y));
		line(g1.x, g1.y, g2.x, g2.y);
		GraphPoint text = toGraphPoint(new MathPoint(p.x + 10, p.y + 10));
		String numString = NUMBER_FORMAT.format(num);
		text(numString, text.x, text.y);
	}

	// draw an arrow
	void arrow(float x1, float y1, float x2, float y2) {
		line(x1, y1, x2, y2);
		pushMatrix();
		translate(x2, y2);
		float a = atan2(x1 - x2, y2 - y1);
		rotate(a);
		line(0, 0, -10, -10);
		line(0, 0, 10, -10);
		popMatrix();
	}

	void drawArrow(MathPoint p1, MathPoint p2) {
		GraphPoint g1 = toGraphPoint(p1);
		GraphPoint g2 = toGraphPoint(p2);
		arrow(g1.x, g1.y, g2.x, g2.y);
	}
	
	

}
